//  Copyright (c) Microsoft Corporation.  All Rights Reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using System.IdentityModel.Selectors;

namespace EAI.SAML
{
    public class EAISamlAdvice
    {
        readonly ImmutableCollection<EAISamlAssertion> assertionList = new ImmutableCollection<EAISamlAssertion>();
        readonly ImmutableCollection<string> assertionIDRefList = new ImmutableCollection<string>();
        readonly ImmutableCollection<Uri> assertionURIRefList = new ImmutableCollection<Uri>();
        readonly ImmutableCollection<EAISamlEncryptedAssertion> encryptedAssertionList = new ImmutableCollection<EAISamlEncryptedAssertion>();


        bool isReadOnly = false;

        public EAISamlAdvice()
        {
        }

        public EAISamlAdvice(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            this.ReadXml(reader, serializer, keyInfoSerializer, outOfBandTokenResolver);
        }

        public IList<EAISamlAssertion> Assertions
        {
            get 
            {
                return this.assertionList; 
            }
        }

        public IList<string> AssertionIDRefs
        {
            get 
            {
                return this.assertionIDRefList; 
            }
        }

        public IList<Uri> AssertionURIRefs
        {
            get 
            {
                return this.assertionURIRefList; 
            }
        }

        void CheckObjectValidity()
        {
            if (this.assertionList.Count == 0 && this.assertionIDRefList.Count == 0 && this.assertionURIRefList.Count == 0 && this.encryptedAssertionList.Count == 0)
                throw new InvalidOperationException("saml:Advice requires at least one of the following types specified. Assertion, EncryptedAssertion, AssertionIDRef and AssertionURIRef.");
        }

        public void MakeReadOnly()
        {
            if (!this.isReadOnly)
            {
                this.assertionList.MakeReadOnly();
                this.assertionIDRefList.MakeReadOnly();
                this.assertionURIRefList.MakeReadOnly();
                this.encryptedAssertionList.MakeReadOnly();
                this.isReadOnly = true;
            }
        }

        public virtual void ReadXml(XmlReader reader, EAISamlSerializer serializer, SecurityTokenSerializer keyInfoSerializer, SecurityTokenResolver outOfBandTokenResolver)
        {
            if (reader == null)
                throw new ArgumentNullException("reader");

            if (serializer == null)
                throw new ArgumentNullException("serializer");

            // SAML Advice is optional element and all its child elements
            // are optional. 
            if (reader.IsEmptyElement)
            {
                reader.MoveToContent();
                reader.Read();
                return;
            }

            reader.Read();
            while (reader.IsStartElement())
            {
                if (reader.IsStartElement(EAISamlConstants.Assertion, EAISamlConstants.Namespace))
                {
                    if ((this.encryptedAssertionList.Count != 0) || (this.assertionIDRefList.Count != 0) || (this.assertionURIRefList.Count != 0))
                        throw new SecurityTokenException("saml:Advice has more than one element type as the child element.");

                    this.assertionList.Add(serializer.LoadAssertion(reader, keyInfoSerializer, outOfBandTokenResolver));
                }
                else if (reader.IsStartElement(EAISamlConstants.EncryptedAssertion, EAISamlConstants.Namespace))
                {
                    if ((this.assertionList.Count != 0) || (this.assertionList.Count != 0) || (this.assertionURIRefList.Count != 0))
                        throw new SecurityTokenException("saml:Advice has more than one element type as the child element.");

                    this.encryptedAssertionList.Add(serializer.LoadEncryptedAssertion(reader, keyInfoSerializer, outOfBandTokenResolver));
                }
                else if (reader.IsStartElement(EAISamlConstants.AssertionIDRef, EAISamlConstants.Namespace))
                {
                    if ((this.assertionList.Count != 0) || (this.encryptedAssertionList.Count != 0) || (this.assertionURIRefList.Count != 0))
                        throw new SecurityTokenException("saml:Advice has more than one element type as the child element.");

                    this.assertionIDRefList.Add(reader.ReadElementString());
                }
                else if (reader.IsStartElement(EAISamlConstants.AssertioURIRef, EAISamlConstants.Namespace))
                {
                    if ((this.assertionIDRefList.Count != 0) || (this.encryptedAssertionList.Count != 0) || (this.assertionList.Count != 0))
                        throw new SecurityTokenException("saml:Advice has more than one element type as the child element.");

                    this.assertionURIRefList.Add(new Uri(reader.ReadElementString()));
                }
                else
                {
                    throw new SecurityTokenException("Unknown child element inside saml:Advice element.");
                }
            }

            reader.ReadEndElement();
        }

        public virtual void WriteXml(XmlWriter writer, SecurityTokenSerializer tokenSerializer)
        {
            CheckObjectValidity();

            if (writer == null)
                throw new ArgumentNullException("writer");

            writer.WriteStartElement(EAISamlConstants.Advice, EAISamlConstants.Namespace);

            if (this.assertionList.Count > 0)
            {
                for (int i = 0; i < this.assertionList.Count; ++i)
                {
                    this.assertionList[i].WriteTo(writer, tokenSerializer);
                }
            }
            else if (this.encryptedAssertionList.Count > 0)
            {
                for (int i = 0; i < this.encryptedAssertionList.Count; ++i)
                {
                    this.encryptedAssertionList[i].WriteXml(writer, tokenSerializer);
                }
            }
            else if (this.assertionIDRefList.Count > 0)
            {
                for (int i = 0; i < this.assertionIDRefList.Count; ++i)
                {
                    writer.WriteStartElement(EAISamlConstants.AssertionIDRef, EAISamlConstants.Namespace);
                    writer.WriteString(this.assertionIDRefList[0]);
                    writer.WriteEndElement();
                }
            }
            else if (this.assertionURIRefList.Count > 0)
            {
                for (int i = 0; i < this.assertionURIRefList.Count; ++i)
                {
                    writer.WriteStartElement(EAISamlConstants.AssertioURIRef, EAISamlConstants.Namespace);
                    writer.WriteString(this.assertionURIRefList[0].AbsoluteUri);
                    writer.WriteEndElement();
                }
            }

            writer.WriteEndElement();
        }

    }
}
